package com.mingqijia.gassafety.authority.dal.provider;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mingqijia.gassafety.authority.api.resp.company.CompanyLiteRespDTO;
import com.mingqijia.gassafety.authority.dal.command.CreateCompanyCommand;
import com.mingqijia.gassafety.authority.dal.command.QueryCompanyCommand;
import com.mingqijia.gassafety.authority.dal.command.RemoveCompanyCommand;
import com.mingqijia.gassafety.authority.dal.command.UpdateCompanyCommand;
import com.mingqijia.gassafety.authority.dal.handler.CompanyTranslator;
import com.mingqijia.gassafety.db.entity.auth.CompanyEntry;
import com.mingqijia.gassafety.db.mapper.auth.CompanyMapper;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.cqrs.QueryHandler;
import com.mingqijia.gassafety.shared.enums.RunningState;
import com.mingqijia.gassafety.shared.enums.YesNoEnum;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 信息CU操作(C-create,U-update)
 * @author Damon S.
 * @version v0.2
 * @date 2021-06-19
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CompanyProvider extends ServiceImpl<CompanyMapper, CompanyEntry> implements QueryHandler<CompanyEntry> {

    private final CompanyTranslator translator;

    public List<CompanyLiteRespDTO> buildHierarchy() {
        QueryWrapper<CompanyEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(CompanyEntry::getIsDeleted, YesNoEnum.N.getCode())
                .eq(CompanyEntry::getState, RunningState.RUNNING.getCode())
                .eq(CompanyEntry::getSpId, ApplicationUtils.getHeaderSpId());
        List<CompanyEntry> entries = this.list(queryClause);

        List<CompanyLiteRespDTO> responses = translator.toLiteRespDTO(entries);
        List<CompanyLiteRespDTO> rootCompanies = responses.stream().filter(
                item -> Objects.equals(item.getParentId(), Constants.PLATFORM_SP)
        ).collect(Collectors.toList());

        for (CompanyLiteRespDTO company : rootCompanies) {
            this.buildCompanyHierarchy(responses, company);
        }
        return rootCompanies;
    }

    public List<CompanyLiteRespDTO> buildHierarchyVcc() {
        QueryWrapper<CompanyEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(CompanyEntry::getIsDeleted, YesNoEnum.N.getCode())
                .eq(CompanyEntry::getState, RunningState.RUNNING.getCode())
                .eq(CompanyEntry::getSpId, ApplicationUtils.getHeaderSpId());
        List<CompanyEntry> entries = this.list(queryClause);

        List<CompanyLiteRespDTO> responses = translator.toLiteRespDTO(entries);
//        List<CompanyLiteRespDTO> rootCompanies = responses.stream().filter(
//                item -> Objects.equals(item.getParentId(), Constants.PLATFORM_SP)
//        ).collect(Collectors.toList());
//
//        for (CompanyLiteRespDTO company : rootCompanies) {
//            this.buildCompanyHierarchy(responses, company);
//        }
//        return rootCompanies;
        return responses;
    }

    public Page<CompanyEntry> findVcc(QueryCompanyCommand command) {
        LambdaQueryWrapper<CompanyEntry> queryClause = new LambdaQueryWrapper<>();

//        Optional.ofNullable(command.getNature()).ifPresent(
//                nature -> queryClause.eq(CompanyEntry::getNature, nature)
//        );
//        Optional.ofNullable(command.getType()).ifPresent(
//                type -> queryClause.in(CompanyEntry::getType, type)
//        );
        if (Objects.nonNull(command.getState()) && !RunningState.NA.equals(command.getState())) {
            queryClause.eq(CompanyEntry::getState, command.getState().getCode());
        }
        if (!CollectionUtils.isEmpty(command.getId())) {
            queryClause.in(CompanyEntry::getId, command.getId());
        }
        if (!CollectionUtils.isEmpty(command.getName())) {
            queryClause.and(buildNameClause(command.getName()));
        }
        // 不返回 平台企业
        queryClause.eq(CompanyEntry::getIsDeleted, YesNoEnum.N.getCode())
//                .eq(CompanyEntry::getSpId, ApplicationUtils.getHeaderSpId())
//                .ne(CompanyEntry::getType, Constants.DICTIONARY_PLATFORM)
                .eq(CompanyEntry::getSource, Constants.INT_ONE)
                .orderByDesc(CompanyEntry::getCreatedAt);
        return this.page(createPage(command), queryClause);
    }

    public Page<CompanyEntry> find(QueryCompanyCommand command) {
        LambdaQueryWrapper<CompanyEntry> queryClause = new LambdaQueryWrapper<>();

        Optional.ofNullable(command.getNature()).ifPresent(
                nature -> queryClause.eq(CompanyEntry::getNature, nature)
        );
        Optional.ofNullable(command.getType()).ifPresent(
                type -> queryClause.in(CompanyEntry::getType, type)
        );
        if (Objects.nonNull(command.getState()) && !RunningState.NA.equals(command.getState())) {
            queryClause.eq(CompanyEntry::getState, command.getState().getCode());
        }
        if (!CollectionUtils.isEmpty(command.getId())) {
            queryClause.in(CompanyEntry::getId, command.getId());
        }
        if (!CollectionUtils.isEmpty(command.getName())) {
            queryClause.and(buildNameClause(command.getName()));
        }
        // 不返回 平台企业
        queryClause.eq(CompanyEntry::getIsDeleted, YesNoEnum.N.getCode())
                .eq(CompanyEntry::getSpId, ApplicationUtils.getHeaderSpId())
                .ne(CompanyEntry::getType, Constants.DICTIONARY_PLATFORM)
                .eq(CompanyEntry::getSource, Constants.INT_ZERO)
                .orderByDesc(CompanyEntry::getCreatedAt);
        return this.page(createPage(command), queryClause);
    }

    public List<CompanyEntry> findByParentIdHierarchy(String parentId) {
        QueryWrapper<CompanyEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(CompanyEntry::getParentId, parentId)
                .eq(CompanyEntry::getState, RunningState.RUNNING.getCode())
                .eq(CompanyEntry::getSource, Constants.INT_ZERO)
                .eq(CompanyEntry::getIsDeleted, YesNoEnum.N.getCode());
        List<CompanyEntry> roots = this.list(queryClause);

        List<CompanyEntry> enterprises = new ArrayList<>(roots);
        for (CompanyEntry entry : roots) {
            if (Objects.equals(entry.getType(), Constants.DICTIONARY_SUBSIDIARY)
                    || Objects.equals(entry.getType(), Constants.DICTIONARY_ENTERPRISE)) {
                continue;
            }
            enterprises.addAll(findByParentIdHierarchy(entry.getId()));
        }
        return enterprises;
    }

    @Transactional(rollbackFor = Exception.class)
    public String create(CreateCompanyCommand command) {
        this.attachOperator2Command(command);

        if (ObjectUtils.isEmpty(command.getState())) {
            command.setState(RunningState.RUNNING);
        }
        if (ObjectUtils.isEmpty(command.getCode())) {
            command.setCode("C" + ApplicationUtils.lenUpperString(5));
        }

        QueryWrapper<CompanyEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(CompanyEntry::getIsDeleted, YesNoEnum.N.getCode())
                .eq(CompanyEntry::getSource, Constants.INT_ZERO)
                .and(qw -> qw.eq(CompanyEntry::getName, command.getName())
                        .or().eq(CompanyEntry::getAlias, command.getAlias()));
        if (!Objects.equals(Constants.INT_ZERO, this.count(queryClause))) {
            throw new GlobalAuthenticationException("企业名称或简称已存在");
        }
        CompanyEntry entry = translator.toEntry(command);
        if (!this.save(entry)) {
            throw new GlobalAuthenticationException("创建企业失败");
        }
        return entry.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean update(UpdateCompanyCommand command) {
        this.attachOperator2Command(command);

        CompanyEntry entry = getById(command.getId());
        if (Objects.isNull(entry)) {
            throw new GlobalAuthenticationException("组织不存在id=" + command.getId());
        }
        // 如果是根集团，以下信息不能修改
        if (Objects.equals(entry.getParentId(), Constants.STR_SLASH)) {
            command.setNature(null); command.setType(null); command.setParentId(null);
        }
        UpdateWrapper<CompanyEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().eq(CompanyEntry::getId, command.getId());

        entry = translator.toEntry(command);
        return this.update(entry, updateClause);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean remove(RemoveCompanyCommand command) {
        this.attachOperator2Command(command);

        UpdateWrapper<CompanyEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().in(CompanyEntry::getId, command.getId())
                .ne(CompanyEntry::getId, Constants.PLATFORM_SP);

        CompanyEntry entry = translator.toEntry(command);
        entry.setIsDeleted(YesNoEnum.Y.isFlag());

        return this.update(entry, updateClause);
    }

    private void buildCompanyHierarchy(List<CompanyLiteRespDTO> sources,
                                       CompanyLiteRespDTO target) {
        target.setChildren(sources.stream().filter(
                item -> Objects.equals(item.getParentId(), target.getId())
        ).collect(Collectors.toList()));
        if (CollectionUtils.isEmpty(target.getChildren())) {
            return;
        }
        for (CompanyLiteRespDTO resp : target.getChildren()) {
            this.buildCompanyHierarchy(sources, resp);
        }
    }

    @NotNull
    private Consumer<LambdaQueryWrapper<CompanyEntry>> buildNameClause(List<String> names) {
        return queryWhere -> {
            for (int index = Constants.INT_ZERO; index < names.size(); index++) {
                if (index < names.size() - Constants.INT_ONE) {
                    queryWhere.like(CompanyEntry::getName, names.get(index)).or();
                    continue;
                }
                queryWhere.like(CompanyEntry::getName, names.get(index));
            }
        };
    }
}
